﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage;

namespace Logger
{
    public class Debug
    {
        public static bool SaveEnabled = true;
        public static bool isThreadMode = false;
        public static void WriteLine(object value)
        {
            WriteLine(value.ToString());
        }

        public static void WriteLine(string message)
        {
            string log = $"[{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") }][{Thread.CurrentThread.ManagedThreadId}] : {message}";
            //Trace.WriteLine(log);

            WriteLog(log + "\r\n");
        }

        public static void WriteToFile(string message)
        {
            WriteLog(message + "\r\n");
        }

        public static void Clear()
        {
            Instance.Reset();
        }

        #region Private Implementation
        private static void WriteLog(string log)
        {
            if (SaveEnabled)
            {
                if (!isThreadMode)
                {
                    Instance.Write(log);
                    return;
                }
                if (Instance.isRunning)
                {
                    Instance.AddLog(log);
                }
                else
                {
                    Instance.Write(log);
                }
                
            }
        }

        private static readonly Lazy<Debug> LazyInstance = new Lazy<Debug>(() =>
        {
            var instance = new Debug();
            instance.Initialize();
            instance.LaunchTaskToFlushLog();
            return instance;

        }, LazyThreadSafetyMode.ExecutionAndPublication);

        public static Debug Instance => LazyInstance.Value;

        private readonly static object stream_lock = new object();
        private readonly Semaphore NotifySemphore = new Semaphore(0, 10000);
        private static Stream stream = null;

        private bool IsFlushTaskRunning = true;

        private ConcurrentQueue<string> logQueue = new ConcurrentQueue<string>();
        private Thread workThread = null;
        private volatile bool isRunning = false;
        private ManualResetEvent waiter = new ManualResetEvent(true);
        private Debug()
        {
            CreateWorkThread();
        }

        public void Reset()
        {
            isRunning = false;
            waiter.WaitOne(300);
        }

        private void CreateWorkThread()
        {
            if (isRunning)
            {
                return;
            }
            isRunning = true;
            workThread = new Thread(() =>
            {
                waiter.Reset();
                while (true)
                {
                   if(!isRunning && logQueue.IsEmpty)
                    {
                        break;
                    }
                    string msg;
                    bool ret = logQueue.TryDequeue(out msg);
                    if (!ret)
                    {
                        Thread.Sleep(20);
                        continue;
                    }
                    Write(msg);
                }
                waiter.Set();

            });
            workThread.IsBackground = true;
            workThread.Start();
        }

        private async void Initialize()
        {
            var filename = "log_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + "_UWP.txt";
            var folder = ApplicationData.Current.LocalFolder;
            try
            {
                var storageFolder = await folder.CreateFolderAsync("logs", CreationCollisionOption.OpenIfExists);
                var file = await storageFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists);

                stream = await file.OpenStreamForWriteAsync();
                //stream.Seek(stream.Length, SeekOrigin.Begin);
            }
            catch (Exception)
            {

            }
        }

        ~Debug()
        {
            IsFlushTaskRunning = false;
        }

        private void AddLog(string msg)
        {
            if (!isRunning)
            {
                return;
            }
            logQueue.Enqueue(msg);
        }

        private void Write(string msg)
        {
            byte[] b = Encoding.UTF8.GetBytes(msg);
            lock (stream_lock)
            {
                stream?.Write(b, 0, b.Count());
            }
            NotifySemphore.Release();
        }

        private async void LaunchTaskToFlushLog()
        {
            IsFlushTaskRunning = true;
            await Task.Run(() =>
            {
                while (IsFlushTaskRunning)
                {
                    NotifySemphore.WaitOne();
                    lock (stream_lock)
                    {
                        stream?.Flush();
                    }
                }
            }).ConfigureAwait(false);
        }
        #endregion
    }
}
